package com.lahuan.config.datasouce;

import com.lahuan.config.aop.mycache.cache.CacheItem;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;


/**
 * 自定义的简化版数据库连接池实现了以下功能:<br/>
 * 缓存连接.在连接关闭时用代理实现了归还连接<br/>
 * 连接池可以设置最大/最小的连接数量<br/>
 * 连接池可以设置连接的最大存活时间,在连接归还时检测<br/>
 * 在连接池已被借光时，如果还申请连接，就会创建新连接返回.<br/>
 * 归还连接时，如果连接池已满就会直接关闭.<br/>
 * 其它接口未和功能实现,仅用于测试和学习.<br/>
 */
@Slf4j
@Data
public class CustomDataSourcePool implements DataSource {
    /**
     * 链接池 在lock时才可以操作这个对象
     */
    private List<Connection> connectionPool = new ArrayList<>();
    /**
     * 记录每个连接的创建时间,用于关闭连接,在lock时才可以操作这个对象
     */
    private ConcurrentHashMap<Connection, Long> connectionCreateStamp = new ConcurrentHashMap<>();
    /**
     * 锁
     */
    Lock lock = new ReentrantLock();


    String driver = "com.mysql.cj.jdbc.Driver";
    String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&transformedBitIsBoolean=true&autoReconnect=true&serverTimezone=Asia/Shanghai&useCursorFetch=true";
    String user = "root";
    String pwd = "123456";
    int minSize = 1;
    int maxSize = 10;
    long maxLive = 30000;


    public CustomDataSourcePool(String driver, String url, String user, String pwd, int minSize, int maxSize, long maxLive) throws Exception {
        this.driver = driver;
        this.url = url;
        this.user = user;
        this.pwd = pwd;
        this.minSize = minSize;
        this.maxSize = maxSize;
        this.maxLive = maxLive;
        init();
    }


    private void init() throws Exception {
        try {
            lock.lock();

            //创建最小连接数个数据库连接对象备用
            for (int i = 0; i < minSize; i++) {
                connectionPool.add(creatConnection());
            }
        } finally {
            lock.unlock();
        }

    }

    private Connection creatConnection() throws SQLException, ClassNotFoundException {
        try {
            lock.lock();
            log.info("创建新连接");
            Class.forName(driver);
            Connection connection = DriverManager.getConnection(url, user, pwd);
            //代理,处理关闭事件
            Connection proxyConnection = (Connection) Proxy.newProxyInstance(CustomDataSourcePool.class.getClassLoader(),
                    new Class[]{Connection.class}, (proxy, method, args) -> {
                        if (!method.getName().equalsIgnoreCase("close")) {
                            return method.invoke(connection, args);
                        } else {
                            //归还
                            lock.lock();
                            try {
                                log.info("归还连接");
                                Long creatTime = connectionCreateStamp.get(connection);
                                long expire = creatTime + maxLive;
                                long now = System.currentTimeMillis();
                                if (expire < now) {
                                    //关闭
                                    connectionCreateStamp.remove(connection);
                                    log.info("连接已超过最大存活时间,关闭连接");
                                    return method.invoke(connection, args);
                                } else {
                                    //放回连接池

                                    if (connectionPool.size() <= maxSize) {
                                        connectionPool.add(connection);
                                    } else {
                                        connectionCreateStamp.remove(connection);
                                        log.info("归还时连接池已满,关闭连接");
                                        return method.invoke(connection, args);
                                    }

                                }
                            } finally {
                                lock.unlock();
                            }
                            return null;
                        }

                    });

            connectionCreateStamp.put(connection, System.currentTimeMillis());
            return proxyConnection;

        } finally {
            lock.unlock();
        }
    }


    @Override
    public Connection getConnection() throws SQLException {
        try {
            lock.lock();
            log.info("获取新链接");
            //连接池还够的话
            if (connectionPool.size() > 0) {
                return connectionPool.remove(0);
            }
            return creatConnection();


        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}